package me.skyun.androidarch.connect;

import android.os.Handler;
import android.os.Looper;

import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import me.skyun.androidarch.common.Utils;
import me.skyun.androidarch.connect.Connection;
import me.skyun.androidarch.connect.Server;

/**
 * Created by skyun on 17/8/21.
 */

public abstract class ConnServer<L extends Closeable, C extends Closeable> extends Thread implements Server {

    protected L mListenSocket;
    private Callback mServerCallback;
    private Connection.Callback mConnectionCallback = new Connection.DefaultCallback();
    private List<Connection> mConnections = new ArrayList<>();

    public ConnServer(Callback serverCallback, Connection.Callback connectionCallback) {
        mServerCallback = serverCallback;
        mConnectionCallback = connectionCallback;
    }

    public void startListen() {
        start();
    }

    protected abstract L createListenSocket();

    protected abstract C accept() throws IOException;

    protected abstract String getAddress(C conSock);

    @Override
    public void run() {
        mListenSocket = createListenSocket();
        while (mListenSocket != null) {
            final C connectSocket;
            try {
                Utils.logMsg("waiting on accept");
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        mServerCallback.onListenStarted();
                    }
                });
                connectSocket = accept();
            } catch (IOException e) {
                Utils.logMsg("Accept failed: " + e.getMessage());
                continue;
            }

            stopListen();
            if (connectSocket == null) {
                Utils.logMsg("Made connection, but socket is null");
                continue;
            }
            Utils.logMsg("Connection from address: " + getAddress(connectSocket));
            Connection connection = new Connection(connectSocket, mConnectionCallback);
            connection.start();
        }
    }

    public void stopListen() {
        if (mListenSocket != null) {
            try {
                mListenSocket.close();
                mListenSocket = null;
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        mServerCallback.onListenStopped();
                    }
                });
            } catch (IOException e) {
                Utils.logMsg("close() of startRun socket failed: " + e.getMessage());
            }
        }
    }

    public void disconnectAll() {
        for (Connection connection : mConnections) {
            connection.disconnect();
        }
    }

    public void stopServer() {
        stopListen();
        disconnectAll();
    }
}
